/*
 * maz_drv_uart.c
 *
 *  Created on: 2020年6月8日
 *      Author: Paul
 */

#include "maz_app_debugcmd.h"
#include "maz_drv_uart.h"

/**
 * "dlvl" variables must be defined if debug message is to be printed through the dlog/dmsg, and "dlvl" must be declared static
 */
static int dlvl = DINF;

#define MAZDRV_DEBUG_UART   MAZDRV_E_USART1

static MAZDRV_UART_CTRL __g_mazdrv_uart_ctrl[MAZDRV_E_UART_MAX] =
{
    [MAZDRV_E_USART1] =
    {
        .use            = MAZDRV_E_UART_USE_ENABLE,
        .cfg.prior      = 0,
        .cfg.iomap      = MAZDRV_E_USART1_IOMAP1_PA9_PA10,
        .cfg.conf       =
        {
            .Instance  = USART1,
            .Init      =
            {
                .BaudRate      = 115200,
                .WordLength    = UART_WORDLENGTH_8B,
                .StopBits      = UART_STOPBITS_1,
                .Parity        = UART_PARITY_NONE,
                .Mode          = UART_MODE_TX_RX,
                .HwFlowCtl     = UART_HWCONTROL_NONE,
                .OverSampling  = UART_OVERSAMPLING_16,
            },
        },
    },
    [MAZDRV_E_USART2] =
    {
        .use        = MAZDRV_E_UART_USE_DISABLE,
    },
    [MAZDRV_E_USART6] =
    {
        .use        = MAZDRV_E_UART_USE_DISABLE,
    },
};

/* Internal Interface Functions */
int MAZ_Drv_uart_clk_enable(MAZDRV_E_UART index);
int MAZ_Drv_uart_ioremap(MAZDRV_E_UART index);
int MAZ_Drv_uart_config(MAZDRV_E_UART index);
int MAZ_Drv_uart_irq_enable(MAZDRV_E_UART index);

int MAZ_Drv_uart_init(void)
{
    MAZDRV_E_UART index = 0;
    MAZDRV_UART_CTRL *ctrl = NULL;

    for (index = MAZDRV_E_USART1; index < MAZDRV_E_UART_MAX; index++)
    {
        ctrl = &__g_mazdrv_uart_ctrl[index];

        if (MAZDRV_E_UART_USE_ENABLE != ctrl->use)
        {
            continue;
        }

        MAZ_Drv_uart_clk_enable(index);
        MAZ_Drv_uart_ioremap(index);
        MAZ_Drv_uart_config(index);
        MAZ_Drv_uart_irq_enable(index);
    }

    return MAZRET_OK;
}

int MAZ_Drv_uart_clk_enable(MAZDRV_E_UART index)
{
    if (index < MAZDRV_E_USART1 || index >= MAZDRV_E_UART_MAX)
    {
        return MAZRET_EINVAL;
    }

    switch (index)
    {
    case MAZDRV_E_USART1:
        __HAL_RCC_USART1_CLK_ENABLE();
        break;
    case MAZDRV_E_USART2:
        __HAL_RCC_USART2_CLK_ENABLE();
        break;
    case MAZDRV_E_USART6:
        __HAL_RCC_USART6_CLK_ENABLE();
        break;
    case MAZDRV_E_UART_MAX:
        // do nothing, for remove warning
        break;
    }

    return MAZRET_OK;
}

int MAZ_Drv_uart_ioremap(MAZDRV_E_UART index)
{
    MAZDRV_UART_CTRL *ctrl = NULL;
    MAZDRV_E_UART_IOMAP map = 0;
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };

    if (index < MAZDRV_E_USART1 || index >= MAZDRV_E_UART_MAX)
    {
        return MAZRET_EINVAL;
    }

    ctrl = &__g_mazdrv_uart_ctrl[index];
    map = ctrl->cfg.iomap;

    if (map < MAZDRV_E_USART1_IOMAP1_PA9_PA10 || map > MAZDRV_E_USART6_IOMAP1_PA11_PA12)
    {
        return MAZRET_EINVAL;
    }

    switch (map)
    {
    case MAZDRV_E_USART1_IOMAP1_PA9_PA10:
        __HAL_RCC_GPIOA_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
        break;

    case MAZDRV_E_USART1_IOMAP2_PB6_PB7:
        __HAL_RCC_GPIOB_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
        break;

    case MAZDRV_E_USART2_IOMAP1_PA2_PA3:
        __HAL_RCC_GPIOA_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
        break;

    case MAZDRV_E_USART6_IOMAP1_PA11_PA12:
        __HAL_RCC_GPIOA_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
        break;
    }

    return MAZRET_OK;
}

int MAZ_Drv_uart_config(MAZDRV_E_UART index)
{
    int ret = 0;
    MAZDRV_UART_CTRL *ctrl = NULL;

    if (index < MAZDRV_E_USART1 || index >= MAZDRV_E_UART_MAX)
    {
        return MAZRET_EINVAL;
    }

    ctrl = &__g_mazdrv_uart_ctrl[index];

    ret = HAL_UART_Init(&ctrl->cfg.conf);
    if(ret != HAL_OK)
    {
        return MAZRET_NG;
    }

    return MAZRET_OK;
}

int MAZ_Drv_uart_irq_enable(MAZDRV_E_UART index)
{
    uint8_t irg_prior = 0;
    MAZDRV_UART_CTRL *ctrl = NULL;

    if (index < MAZDRV_E_USART1 || index >= MAZDRV_E_UART_MAX)
    {
        return MAZRET_EINVAL;
    }

    ctrl = &__g_mazdrv_uart_ctrl[index];
    irg_prior = ctrl->cfg.prior;

    if (irg_prior < 0 || irg_prior > 15)
    {
        return MAZRET_EINVAL;
    }

    switch (index)
    {
    case MAZDRV_E_USART1:
        HAL_NVIC_SetPriority(USART1_IRQn, irg_prior, 0);
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        break;
    case MAZDRV_E_USART2:
        HAL_NVIC_SetPriority(USART2_IRQn, irg_prior, 0);
        HAL_NVIC_EnableIRQ(USART2_IRQn);
        break;
    case MAZDRV_E_USART6:
        HAL_NVIC_SetPriority(USART6_IRQn, irg_prior, 0);
        HAL_NVIC_EnableIRQ(USART6_IRQn);
        break;
    case MAZDRV_E_UART_MAX:
        // do nothing, for remove warning
        break;
    }

    /* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(&ctrl->cfg.conf, UART_IT_RXNE);

    return MAZRET_OK;
}

int __io_putchar(int ch)
{
    UART_HandleTypeDef *huart = &__g_mazdrv_uart_ctrl[MAZDRV_DEBUG_UART].cfg.conf;
    while(!(huart->Instance->SR & (1 << 7)));
    huart->Instance->DR = (ch & (uint8_t)0xFF);
    return (ch);
}

void USART1_IRQHandler(void)
{
    uint8_t tmp;
    uint32_t idle = 0;
    UART_HandleTypeDef *huart = &__g_mazdrv_uart_ctrl[MAZDRV_DEBUG_UART].cfg.conf;
    MAZAPP_DEBUGCMD_CTRL *ctrl = &g_mazapp_debugcmd;

    uint32_t isrflags = READ_REG(huart->Instance->SR);
    uint32_t cr1its = READ_REG(huart->Instance->CR1);

    /* 判断中断源, 启用了串口接收中断, 并且串口接收寄存器非空 */
    if ((cr1its & USART_CR1_RXNEIE) != RESET && ((isrflags & USART_SR_RXNE) != RESET))
    {
        /* 串口配置必须配置为八位数据模式, 并且没有启用奇偶校验位 */
        if ((huart->Init.WordLength == UART_WORDLENGTH_8B) && (huart->Init.Parity == UART_PARITY_NONE))
        {
            /* 读取 DR 寄存器会自动将 RXNE 标志清除 */
            tmp = (uint8_t) (huart->Instance->DR & (uint8_t) 0x00FF);

            /* 队列有剩余空间, 则将串口接收的数据推到队列中, 没有剩余空间, 数据将直接丢弃 */
            idle = MAZAPP_DEBUGCMD_IDLE_LEN(ctrl->priv.r, ctrl->priv.w, MAZAPP_DEBUGCMD_TOTAL_LEN);
            if (idle > 0)
            {
                ctrl->priv.buf[ctrl->priv.w] = tmp;
                ctrl->priv.w++;
                if(ctrl->priv.w == MAZAPP_DEBUGCMD_TOTAL_LEN)
                {
                    ctrl->priv.w = 0;
                }
            }
        }
    }
}

int MAZ_DBGCMD_uart_dump_reg(void *cmd, char *param)
{
    int index;
    UART_HandleTypeDef *huart = NULL;

    sscanf(param, "%d", &index);

    if (index < MAZDRV_E_USART1 || index >= MAZDRV_E_UART_MAX)
    {
        dmsg(DERR, "index = %d\r\n", index);
        return MAZRET_EINVAL;
    }

    huart = &__g_mazdrv_uart_ctrl[index].cfg.conf;
    dlog(DINF, "SR   = 0x%08lx", huart->Instance->SR);
    dlog(DINF, "DR   = 0x%08lx", huart->Instance->DR);
    dlog(DINF, "BRR  = 0x%08lx", huart->Instance->BRR);
    dlog(DINF, "CR1  = 0x%08lx", huart->Instance->CR1);
    dlog(DINF, "CR2  = 0x%08lx", huart->Instance->CR2);
    dlog(DINF, "CR3  = 0x%08lx", huart->Instance->CR3);
    dlog(DINF, "GTPR = 0x%08lx", huart->Instance->GTPR);

    return MAZRET_OK;
}
